home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2001 December / pcwk12201b.iso / Wersje pelne i specjalne / Winamp 2.77 i 3.0beta / wasabi-sdk_beta1.exe / studio / common / treewnd.cpp < prev    next >
C/C++ Source or Header  |  2001-10-08  |  38KB  |  1,600 lines

  1. /*
  2.  
  3.   Nullsoft WASABI Source File License
  4.  
  5.   Copyright 1999-2001 Nullsoft, Inc.
  6.  
  7.     This software is provided 'as-is', without any express or implied
  8.     warranty.  In no event will the authors be held liable for any damages
  9.     arising from the use of this software.
  10.  
  11.     Permission is granted to anyone to use this software for any purpose,
  12.     including commercial applications, and to alter it and redistribute it
  13.     freely, subject to the following restrictions:
  14.  
  15.     1. The origin of this software must not be misrepresented; you must not
  16.        claim that you wrote the original software. If you use this software
  17.        in a product, an acknowledgment in the product documentation would be
  18.        appreciated but is not required.
  19.     2. Altered source versions must be plainly marked as such, and must not be
  20.        misrepresented as being the original software.
  21.     3. This notice may not be removed or altered from any source distribution.
  22.  
  23.  
  24.   Brennan Underwood
  25.   brennan@nullsoft.com
  26.  
  27. */
  28.  
  29. #include "treewnd.h"
  30. #include "canvas.h"
  31. #include "stack.h"
  32. #include "scrollbar.h"
  33. #include "sepwnd.h"
  34. #include "skinclr.h"
  35. #include "notifmsg.h"
  36. #include "../studio/api.h"
  37.  
  38. #define DEF_TEXT_SIZE 14
  39. #define CHILD_INDENT itemHeight
  40. #define X_SHIFT 2
  41. #define Y_SHIFT 2
  42. #define DRAG_THRESHOLD 4
  43.  
  44. #define TIMER_EDIT_DELAY 1000
  45. #define TIMER_EDIT_ID    1249
  46.  
  47. ///////////////////////////////////////////////////////////////////////////////
  48. // TreeWnd
  49. ///////////////////////////////////////////////////////////////////////////////
  50.  
  51. static SkinColor textcolor("studio.tree.text");
  52. static SkinColor drophilitecolor("studio.tree.hiliteddrop");
  53. static SkinColor selectedcolor("studio.tree.selected");
  54.  
  55. int CompareTreeItem::compareItem(TreeItem *p1, TreeItem *p2) {
  56.   return p1->getTree()->compareItem(p1, p2);
  57. }
  58.  
  59. TreeWnd::TreeWnd() {
  60.     tabClosed = NULL;
  61.     tabOpen = NULL;
  62.     linkTopBottom = NULL;
  63.     linkTopRight = NULL;
  64.     linkTopRightBottom = NULL;
  65.     linkTabTopBottom = NULL;
  66.     linkTabTopRight = NULL;
  67.     linkTabTopRightBottom = NULL;
  68.     curSelected = NULL;
  69.     mousedown_item = NULL;
  70.   hitItem = NULL;
  71.   draggedItem = NULL;
  72.   tipitem = NULL;
  73.   edited = NULL;
  74.   editwnd = NULL;
  75.     metrics_ok = FALSE;
  76.   setSorted(TRUE);
  77.   setFontSize(DEF_TEXT_SIZE);
  78.   redraw = TRUE;
  79.   prevbdownitem = NULL;
  80.   autoedit=0;
  81.   autocollapse=1;
  82. }
  83.  
  84. TreeWnd::~TreeWnd() {
  85.   // delete all root items
  86.   deleteAllItems();
  87.  
  88. // why, francis? why? --BU
  89. // coz eventho i know it works, i can't convince myself that deleting without checking isn't bad... somehow :) -FG
  90.     if (tabOpen) delete tabOpen;
  91.     if (tabClosed) delete tabClosed;
  92.     if (linkTopBottom) delete linkTopBottom;
  93.     if (linkTopRight) delete linkTopRight;
  94.     if (linkTopRightBottom) delete linkTopRightBottom;
  95.     if (linkTabTopBottom) delete linkTabTopBottom;
  96.     if (linkTabTopRight) delete linkTabTopRight;
  97.     if (linkTabTopRightBottom) delete linkTabTopRightBottom;
  98.  
  99.   drawList.removeAll();
  100. }
  101.  
  102. int TreeWnd::onInit() {
  103.     TREEWND_PARENT::onInit();
  104.  
  105.     setBgBitmap("studio.tree.background");
  106.     setLineHeight(itemHeight);
  107.  
  108.     tabClosed = new SkinBitmap("studio.tree.tab.closed");
  109.     tabOpen = new SkinBitmap("studio.tree.tab.open");
  110.  
  111.     linkTopBottom = new SkinBitmap("studio.tree.link.top.bottom");
  112.     linkTopRight = new SkinBitmap("studio.tree.link.top.right");
  113.     linkTopRightBottom = new SkinBitmap("studio.tree.link.top.rightBottom");
  114.     linkTabTopBottom = new SkinBitmap("studio.tree.link.tab.top.bottom");
  115.     linkTabTopRight = new SkinBitmap("studio.tree.link.tab.top.right");
  116.     linkTabTopRightBottom = new SkinBitmap("studio.tree.link.tab.top.rightBottom");
  117.  
  118.     return 1;
  119. }
  120.  
  121. void TreeWnd::setRedraw(BOOL r) {
  122.   int old = redraw;
  123.   redraw = r;
  124.   if (!old && redraw)
  125.     invalidate();
  126. }
  127.  
  128. int TreeWnd::onPaint(Canvas *canvas) {
  129.  
  130.   PaintCanvas paintcanvas;
  131.   PaintBltCanvas paintbcanvas;
  132.  
  133.   if (canvas == NULL) {
  134.     if (needDoubleBuffer()) {
  135.       if (!paintbcanvas.beginPaintNC(this)) return 0;
  136.       canvas = &paintbcanvas;
  137.     } else {
  138.       if (!paintcanvas.beginPaint(this)) return 0;
  139.       canvas = &paintcanvas;
  140.     }
  141.   }
  142.   TREEWND_PARENT::onPaint(canvas);
  143.  
  144. /* uncomment if you add columns or anything that should be not be drawn over by onPaint in which case you'll have to clip->subtract(your_region)
  145.   Region *clip = new Region();
  146.   canvas->getClipRgn(clip); */
  147.  
  148.   RECT r;
  149.   getNonClientRect(&r);
  150.  
  151.   int y = -getScrollY()+Y_SHIFT+r.top;
  152.   int x = -getScrollX()+X_SHIFT;
  153.  
  154.   canvas->pushTextColor(textcolor);
  155.   canvas->setTextOpaque(FALSE);
  156.   canvas->pushTextSize(getFontSize());
  157.  
  158.   firstItemVisible = NULL;
  159.   lastItemVisible = NULL;
  160.  
  161.   ensureMetricsValid();
  162.  
  163.   //drawSubItems(canvas, x, &y, items, r.top, r.bottom, 0);
  164.   drawItems(canvas);
  165.  
  166.   canvas->popTextColor();
  167.   canvas->popTextSize();
  168.  
  169.   canvas->selectClipRgn(NULL); // reset cliping region - NEEDED;
  170.  
  171. //  delete clip; uncomment if necessary
  172.  
  173.   return 1;
  174. }
  175.  
  176. void TreeWnd::drawItems(Canvas *canvas) {
  177.  
  178.   RECT r, c, ir;
  179.   Region *orig=NULL;
  180.   getClientRect(&r);
  181.   if (GetClipBox(canvas->getHDC(), &c) == NULLREGION) {
  182.     getClientRect(&c);
  183.     orig = new Region(&c);
  184.   } else
  185.     orig = new Region(canvas);
  186.  
  187.   int first = ((c.top-r.top) + getScrollY() - Y_SHIFT) / itemHeight;
  188.   int last = ((c.bottom-r.top) + getScrollY() - Y_SHIFT) / itemHeight + 1;
  189.   POINT pt;
  190.   TreeItem *item;
  191.   BOOL hastab;
  192.  
  193.   for (int i=first;i<=last;i++) {
  194.  
  195.     if (i >= drawList.getNumItems()) break;
  196.  
  197.     item = drawList[i];
  198.     if (!item) continue;
  199.     item->getCurRect(&ir);
  200.     pt.x = X_SHIFT+item->getIndent()*itemHeight - getScrollX();//ir.left;
  201.     pt.y = ir.top;
  202.  
  203.     // if we need the +/- icon and any of the link lines, draw them
  204.       if (item->needTab()) {
  205. //        pt.x += itemHeight;
  206.       RECT _r={pt.x-itemHeight, pt.y, pt.x, pt.y+itemHeight};
  207.         (item->isCollapsed() ? tabClosed : tabOpen)->stretchToRectAlpha(canvas, &_r);
  208.         hastab=TRUE;
  209.       } else hastab = FALSE;
  210.  
  211.       int indent = item->getIndent();
  212.  
  213.       for (int j=0;j<indent;j++) {
  214.       RECT _r={pt.x-itemHeight*(j+1), pt.y, pt.x-itemHeight*j, pt.y+itemHeight};
  215.       int l = getLinkLine(item, j);
  216.           if (l == (LINK_RIGHT | LINK_TOP)) {
  217.             ((hastab && j == 0) ? linkTabTopRight : linkTopRight)->stretchToRectAlpha(canvas, &_r);
  218.           }
  219.           if (l == (LINK_RIGHT | LINK_TOP | LINK_BOTTOM)) {
  220.             ((hastab && j == 0) ? linkTabTopRightBottom : linkTopRightBottom)->stretchToRectAlpha(canvas, &_r);
  221.           }
  222.           if (l == (LINK_BOTTOM | LINK_TOP)) {
  223.             ((hastab && j == 0) ? linkTabTopBottom : linkTopBottom)->stretchToRectAlpha(canvas, &_r);
  224.           }
  225.       }
  226.  
  227.     item->customDraw(canvas, pt, itemHeight, (pt.x+getScrollX())-r.left-X_SHIFT, r);
  228.   }
  229.  
  230.   if (orig) delete orig;
  231. }
  232.  
  233. TreeItem *TreeWnd::hitTest(int x, int y) {
  234.   POINT pt={x,y};
  235.   return hitTest(pt);
  236. }
  237.  
  238. TreeItem *TreeWnd::hitTest(POINT pt) {
  239.  
  240.   RECT r, ir;
  241.   getClientRect(&r);
  242.  
  243.   int first = (getScrollY() - Y_SHIFT) / itemHeight;
  244.   int last = ((r.bottom-r.top) + getScrollY() - Y_SHIFT) / itemHeight + 1;
  245.   TreeItem *item;
  246.  
  247.   for (int i=first;i<=last;i++) {
  248.  
  249.     if (i >= drawList.getNumItems()) break;
  250.  
  251.     item = drawList.enumItem(i);
  252.     
  253.     if (item)
  254.     {
  255.       item->getCurRect(&ir);
  256.       if (PtInRect(&ir, pt)) //TODO: implement in std.cpp
  257.           return item;
  258.     }
  259.  
  260.   }
  261.  
  262.   return NULL;  
  263. }
  264.  
  265. void TreeWnd::getMetrics(int *numItemsShown, int *mWidth) {
  266.   *mWidth=0;
  267.   *numItemsShown=0;
  268.   drawList.removeAll();
  269.   countSubItems(drawList, &items, X_SHIFT, numItemsShown, mWidth, 0);
  270. }
  271.  
  272. void TreeWnd::countSubItems(PtrList<TreeItem> &drawlist, TreeItemList *_list, int indent, int *count, int *maxwidth, int z) {
  273.  
  274.   int w;
  275.   TreeItemList &list = *_list;
  276.  
  277.   for (int i=0;i<list.getNumItems();i++) {
  278.  
  279.     TreeItem *nextitem = list[i];
  280.  
  281.     w = nextitem->getItemWidth(itemHeight, indent-X_SHIFT);
  282.     if (indent+w > *maxwidth) *maxwidth = w+indent;
  283.  
  284.     int j = indent-(nextitem->needTab() ? itemHeight : 0);
  285.     int k;
  286.     k = indent + w;
  287.  
  288.     nextitem->setCurRect(j, Y_SHIFT+(*count * itemHeight), k, Y_SHIFT+((*count+1) * itemHeight), z);
  289.     (*count)++;
  290.  
  291.     drawlist.addItem(nextitem);
  292.  
  293.     if (nextitem->isExpanded())
  294.       countSubItems(drawlist, nextitem->subitems, indent+CHILD_INDENT, count, maxwidth, z+1);
  295.   }
  296. }
  297.  
  298. void TreeWnd::timerCallback(int c) {
  299.   switch (c) {
  300.     case TIMER_EDIT_ID:
  301.       prevbdownitem = NULL;
  302.       killTimer(TIMER_EDIT_ID);
  303.       break;
  304.     default:
  305.       TREEWND_PARENT::timerCallback(c);
  306.   }
  307. }
  308.  
  309. int TreeWnd::onLeftButtonDown(int x, int y) {
  310.  
  311.   if (edited)
  312.     cancelEditLabel(1);
  313.  
  314.   POINT pt={x,y};
  315.   TreeItem *item = hitTest(pt);
  316.  
  317.   if (item) {
  318.     mousedown_item = item;
  319.     mousedown_anchor.x = pt.x;
  320.     mousedown_anchor.y = pt.y;
  321.     mousedown_dragdone = FALSE;
  322.     // only do expand/collapse if was already selected
  323.     setCurItem(item, autocollapse?(curSelected == item):0, FALSE);
  324.     beginCapture();
  325.   }
  326.  
  327.   return 1;
  328. }
  329.  
  330. int TreeWnd::onLeftButtonUp(int x, int y) {
  331.   if (getCapture())
  332.     endCapture();
  333.   TREEWND_PARENT::onLeftButtonUp(x, y);
  334.   POINT pt={x,y};
  335.   TreeItem *item = hitTest(pt);
  336.   if (autoedit && item == mousedown_item && item == prevbdownitem) 
  337.     setCurItem(item, FALSE, TRUE);
  338.   else
  339.     if (autoedit) {
  340.       prevbdownitem = getCurItem();
  341.       setTimer(TIMER_EDIT_ID, TIMER_EDIT_DELAY);
  342.     }
  343.  
  344.   mousedown_item = NULL;
  345.   return 1;
  346. }
  347.  
  348. int TreeWnd::onRightButtonUp(int x, int y){
  349.   TREEWND_PARENT::onRightButtonUp(x, y);
  350.   POINT pos={x,y};
  351.   TreeItem *ti = hitTest(pos);
  352.   if (ti != NULL) {
  353.     selectItem(ti);
  354.     return ti->onContextMenu(pos.x, pos.y);
  355.   } else {
  356.     return onContextMenu(x, y);
  357.   }
  358.   return 0;
  359. }
  360.  
  361. int TreeWnd::onMouseMove(int x, int y) {
  362.  
  363.   TREEWND_PARENT::onMouseMove(x, y);
  364.  
  365.   POINT pt={x,y};
  366.  
  367.   if (mousedown_item) {
  368.     if (!mousedown_dragdone && (ABS(pt.x - mousedown_anchor.x) > DRAG_THRESHOLD || ABS(pt.y - mousedown_anchor.y) > DRAG_THRESHOLD)) {
  369.       mousedown_dragdone = TRUE;
  370.       if (getCapture())
  371.         endCapture();
  372.       onBeginDrag(mousedown_item);
  373.     }
  374.   } else {
  375.     TreeItem *item = hitTest(pt);
  376.     if (item) {
  377.       if (tipitem != item) {
  378.       tipitem = item;
  379.       RECT r;
  380.       RECT c;
  381.       getClientRect(&c);
  382.       item->getCurRect(&r);
  383.       if (r.right > c.right || r.bottom > c.bottom || r.top < c.top || r.left < c.left)
  384.         setLiveTip(item->getLabel());
  385.       }
  386.     } else {
  387.       setTip(getTip());
  388.       tipitem = NULL;
  389.     }
  390.   }
  391.  
  392.   return 1;
  393. }
  394.  
  395. int TreeWnd::onLeftButtonDblClk(int x, int y) {
  396.   TreeItem *item = hitTest(x, y);
  397.   if (item == NULL) return 0;
  398.   return item->onLeftDoubleClick();
  399. }
  400.  
  401. int TreeWnd::onRightButtonDblClk(int x, int y) {
  402.   TreeItem *item = hitTest(x, y);
  403.   if (item == NULL) return 0;
  404.   return item->onRightDoubleClick();
  405. }
  406.  
  407. void TreeWnd::setTip(const char *tip) {
  408.   defaultTip = tip;
  409.   setLiveTip(defaultTip);
  410. }
  411.  
  412. void TreeWnd::setLiveTip(const char *tip) {
  413.   TREEWND_PARENT::setTip(tip);
  414. }
  415.  
  416. const char *TreeWnd::getLiveTip() {
  417.   return TREEWND_PARENT::getTip();
  418. }
  419.  
  420. const char *TreeWnd::getTip() {
  421.   return defaultTip;
  422. }
  423.  
  424. int TreeWnd::onBeginDrag(TreeItem *treeitem) {
  425.   char title[WA_MAX_PATH];
  426.   // item calls addDragItem()
  427.   if (!treeitem->onBeginDrag(title)) return 0;
  428.   ASSERT(draggedItem == NULL);
  429.   draggedItem = treeitem;
  430.   if (*title != 0) setSuggestedDropTitle(title);
  431.   handleDrag();
  432.   return 1;
  433. }
  434.  
  435. int TreeWnd::dragEnter(RootWnd *sourceWnd) {
  436.   // uh... we don't know yet, but we can accept drops in general
  437.   hitItem = NULL;
  438.   return 1;
  439. }
  440.  
  441. int TreeWnd::dragOver(int x, int y, RootWnd *sourceWnd) {
  442.   POINT pos={x,y};
  443.   screenToClient(&pos);
  444.   TreeItem *prevItem;
  445.  
  446.   prevItem = hitItem;
  447.   hitItem = hitTest(pos);
  448.  
  449.   // no dropping on yourself! :)
  450.   if (hitItem == draggedItem) hitItem = NULL;
  451.  
  452.   // unselect previous item
  453.   if (prevItem != hitItem && prevItem != NULL) {
  454.     unhiliteDropItem(prevItem);
  455.     repaint(); // commit invalidation of unhilited item so no trouble with scrolling
  456.     prevItem->dragLeave(sourceWnd);
  457.   }
  458.  
  459.  
  460.   RECT r;
  461.   getClientRect(&r);
  462.   if (pos.y < r.top + 16) {
  463.     if (getScrollY() >= 0) {
  464.       scrollToY(max(0, getScrollY()-itemHeight));
  465.     }
  466.   } else if (pos.y > r.bottom - 16) {
  467.     if (getScrollY() < getMaxScrollY()) {
  468.       scrollToY(min(getMaxScrollY(), getScrollY()+itemHeight));
  469.     }
  470.   }
  471.  
  472.   if (hitItem != NULL) {
  473.     // hilight it
  474.     if (prevItem != hitItem) {
  475.       hiliteDropItem(hitItem);
  476.       repaint(); // commit invalidation of hilited so no trouble with scrolling
  477.     }
  478.   } 
  479.  
  480.   if (hitItem == NULL) return defaultDragOver(x, y, sourceWnd);
  481.  
  482.   // ask the item if it can really accept such a drop
  483.   return hitItem->dragOver(sourceWnd);
  484. }
  485.  
  486. int TreeWnd::dragLeave(RootWnd *sourceWnd) {
  487.   if (hitItem != NULL) {
  488.     unhiliteDropItem(hitItem);
  489.     hitItem->dragLeave(sourceWnd);
  490.   }
  491.   hitItem = NULL;
  492.   return 1;
  493. }
  494.  
  495. int TreeWnd::dragDrop(RootWnd *sourceWnd, int x, int y) {
  496.   int res;
  497.   if (hitItem == NULL) return defaultDragDrop(sourceWnd, x, y);
  498.   // unhilite the dest
  499.   unhiliteDropItem(hitItem);
  500.   // the actual drop
  501.   res = hitItem->dragDrop(sourceWnd);
  502.   if (res) {
  503.     onItemRecvDrop(hitItem);
  504.   }
  505.   hitItem = NULL;
  506.   return res;
  507. }
  508.  
  509. int TreeWnd::dragComplete(int success) {
  510.   int ret;
  511.   ASSERT(draggedItem != NULL);
  512.   ret = draggedItem->dragComplete(success);
  513.   draggedItem = NULL;
  514.   return ret;
  515. }
  516.  
  517. void TreeWnd::hiliteDropItem(TreeItem *item) {
  518.   if (item)
  519.     item->setHilitedDrop(TRUE);
  520. }
  521.  
  522. void TreeWnd::hiliteItem(TreeItem *item) {
  523.   if (item)
  524.     item->setHilited(TRUE);
  525. }
  526.  
  527. void TreeWnd::selectItem(TreeItem *item) {
  528.   setCurItem(item, FALSE);
  529. }
  530.  
  531. void TreeWnd::selectItemDeferred(TreeItem *item) {
  532.   postDeferredCallback(DC_SETITEM, (int)item);
  533. }
  534.  
  535. void TreeWnd::delItemDeferred(TreeItem *item) {
  536.   postDeferredCallback(DC_DELITEM, (int)item);
  537. }
  538.  
  539. void TreeWnd::unhiliteItem(TreeItem *item) {
  540.   if (item)
  541.     item->setHilited(FALSE);
  542. }
  543.  
  544. void TreeWnd::unhiliteDropItem(TreeItem *item) {
  545.   if (item)
  546.     item->setHilitedDrop(FALSE);
  547. }
  548.  
  549. void TreeWnd::setCurItem(TreeItem *item, BOOL expandCollapse, BOOL editifselected) {
  550.   if (curSelected && curSelected != item) {
  551.     curSelected->setSelected(FALSE);
  552.   }
  553.   if (item) {
  554.     curSelected = item;
  555.     item->setSelected(TRUE, expandCollapse, editifselected);
  556.     setSlidersPosition();
  557.   }
  558. }
  559.  
  560. // Returns the current tree width in pixels
  561. int TreeWnd::getContentsWidth() {
  562.   ensureMetricsValid();
  563.   return maxWidth;
  564. }
  565.  
  566. // Returns the current tree height in pixels
  567. int TreeWnd::getContentsHeight() {
  568.   ensureMetricsValid();
  569.   return maxHeight;
  570. }
  571.  
  572. void TreeWnd::ensureMetricsValid() {
  573.   if (metrics_ok) return;
  574.   int n;
  575.   getMetrics(&n, &maxWidth);
  576.   maxWidth += X_SHIFT*2;
  577.   maxHeight = n*itemHeight+Y_SHIFT*2;
  578.   metrics_ok = TRUE;
  579.   setSlidersPosition();
  580. }
  581.  
  582. // Gets notification from sliders
  583. int TreeWnd::childNotify(RootWnd *child, int msg, int param1, int param2) {
  584.   switch (msg) {
  585.     case CHILD_EDIT_DATA_MODIFIED:
  586.       if (child == editwnd && editwnd != NULL) {
  587.         endEditLabel(editbuffer);
  588.         return 1;
  589.       }
  590.       break;
  591.     case CHILD_EDIT_CANCEL_PRESSED:
  592.       if (child == editwnd && editwnd != NULL) {
  593.         cancelEditLabel();
  594.         return 1;
  595.       }
  596.       break;
  597.     case UMSG_EDIT_UPDATE:
  598.       if (child == editwnd && editwnd != NULL) {
  599.         editUpdate();
  600.         return 1;
  601.       }
  602.       break;
  603.   }
  604.  
  605.   return TREEWND_PARENT::childNotify(child, msg, param1, param2);
  606. }
  607.  
  608. void TreeWnd::editUpdate() {
  609.   ASSERT(edited != NULL && editwnd != NULL);
  610.   if (!edited || !editwnd) return;
  611.   int w = editwnd->getTextLength()+16;
  612.   RECT i, r, e;
  613.   edited->getCurRect(&i);
  614.   getClientRect(&r);
  615.   editwnd->getClientRect(&e);
  616.   e.left += i.left;
  617.   e.right += i.left;
  618.   e.top += i.top;
  619.   e.bottom += i.top;
  620.   e.right = i.left+w;
  621.   e.right = min(r.right - X_SHIFT, e.right);
  622.   editwnd->resize(&e);
  623.   editwnd->invalidate();
  624. }
  625.  
  626. TreeItem *TreeWnd::addTreeItem(TreeItem *item, TreeItem *par, int _sorted, int haschildtab) {
  627.  
  628.   ASSERT(item != NULL);
  629.   ASSERTPR(item->getTree() == NULL, "can't transplant TreeItems");
  630.  
  631.   item->setSorted(_sorted);
  632.   item->setChildTab(haschildtab && par != NULL);
  633.   item->linkTo(par);
  634.   item->setTree(this);
  635.  
  636.   if (par == NULL)
  637.     items.addItem(item);
  638.  
  639.   metrics_ok = FALSE;
  640.  
  641.   if (redraw)
  642.     invalidate();
  643.  
  644.   item->onTreeAdd();
  645.  
  646.   return item;
  647. }
  648.  
  649. int TreeWnd::removeTreeItem(TreeItem *item) {
  650.   ASSERT(item != NULL);
  651.   ASSERT(item->getTree() == this);
  652.   if (item->isSelected()) item->setSelected(FALSE);
  653.   if (curSelected == item) curSelected = NULL;
  654. //CUT  item->deleteSubitems();
  655.   TreeItem *par = item->getParent();
  656.   if (!par) { // is root item ?
  657.     ASSERT(items.haveItem(item));
  658.     items.removeItem(item);
  659.   } else {
  660.     if (!par->removeSubitem(item))
  661.       return 0;
  662.   }
  663.   metrics_ok = FALSE;
  664.   drawList.removeItem(item);
  665.   if (redraw)
  666.     invalidate();
  667.  
  668.   item->setTree(NULL);
  669.   item->onTreeRemove();
  670.   
  671.   return 1;
  672. }
  673.  
  674. void TreeWnd::moveTreeItem(TreeItem *item, TreeItem *newparent) {
  675.   ASSERT(item != NULL);
  676.   ASSERTPR(item->getTree() == this, "can't move between trees (fucks up Freelist)");
  677.   removeTreeItem(item);
  678.   addTreeItem(item, newparent);
  679. }
  680.  
  681. void TreeWnd::deleteAllItems() {
  682.   BOOL save_redraw = redraw;
  683.   setRedraw(FALSE);
  684.   
  685.   TreeItem *item;
  686.   while ((item = enumRootItem(0)) != NULL)
  687.     delete item;
  688.  
  689.   setRedraw(save_redraw);
  690. }
  691.  
  692. void TreeWnd::setSorted(int dosort) {
  693.   items.setAutoSort(dosort);
  694. }
  695.  
  696. BOOL TreeWnd::getSorted() {
  697.   return items.getAutoSort();
  698. }
  699.  
  700. void TreeWnd::sortTreeItems() {
  701.   items.sort(TRUE);
  702.   metrics_ok = FALSE;
  703.   if (redraw)
  704.     invalidate();
  705. }
  706.  
  707. TreeItem *TreeWnd::getSibling(TreeItem *item) {
  708.   for (int i=0;i<items.getNumItems();i++) {
  709.     if (items[i] == item) {
  710.       if (i == items.getNumItems()-1) return NULL;
  711.       return items[i+1];
  712.     }
  713.   }
  714.   return NULL;
  715. }
  716.  
  717. void TreeWnd::setAutoCollapse(BOOL doautocollase) {
  718.   autocollapse=doautocollase;
  719. }
  720.  
  721. int TreeWnd::onContextMenu(int x, int y) {
  722.   POINT pos={x,y};
  723.   screenToClient(&pos);
  724.   TreeItem *ti = hitTest(pos);
  725.   if (ti != NULL) {
  726.     selectItem(ti);
  727.     return ti->onContextMenu(x, y);
  728.   }
  729.   return 0;
  730. }
  731.  
  732. int TreeWnd::onDeferredCallback(int param1, int param2) {
  733.   switch (param1) {
  734.     case DC_SETITEM:
  735.       setCurItem((TreeItem *)param2, FALSE);
  736.     return 1;
  737.     case DC_DELITEM:
  738.       delete (TreeItem *)param2;
  739.     return 1;
  740.     case DC_EXPAND:
  741.       expandItem((TreeItem *)param2);
  742.     return 1;
  743.     case DC_COLLAPSE:
  744.       collapseItem((TreeItem *)param2);
  745.     return 1;
  746.   }
  747.   return 0;
  748. }
  749.  
  750. int TreeWnd::getNumRootItems() {
  751.   return items.getNumItems();
  752. }
  753.  
  754. TreeItem *TreeWnd::enumRootItem(int which) {
  755.   return items[which];
  756. }
  757.  
  758. void TreeWnd::invalidateMetrics() {
  759.   metrics_ok = FALSE;
  760. }
  761.  
  762. int TreeWnd::getLinkLine(TreeItem *item, int level) {
  763.  
  764.   ASSERT(item != NULL);
  765.  
  766.   int l = 0;
  767.   int r = 0;
  768.  
  769.   if (item->parent == NULL)
  770.     return 0;
  771.  
  772.   TreeItem *cur=item;
  773.  
  774.   while (cur->getParent() && l < level) {
  775.     cur = cur->getParent();
  776.     l++;
  777.   }
  778.  
  779.   if (cur->getSibling()) r |= LINK_BOTTOM | LINK_TOP;
  780.   if (level == 0) r |= LINK_RIGHT;
  781.   if (level == 0 && cur->getParent()) r |= LINK_TOP;
  782.  
  783.   return r;
  784.  
  785. }
  786.  
  787. int TreeWnd::onMouseWheelDown(int clicked, int lines) {
  788.   if (!clicked)
  789.     scrollToY(min(getMaxScrollY(), getScrollY()+itemHeight));
  790.   else
  791.     scrollToX(min(getMaxScrollX(), getScrollX()+itemHeight));
  792.   return 1;
  793. }
  794.  
  795. int TreeWnd::onMouseWheelUp(int clicked, int lines) {
  796.   if (!clicked)
  797.     scrollToY(max(0, getScrollY()-itemHeight));
  798.   else
  799.     scrollToX(max(0, getScrollX()-itemHeight));
  800.   return 1;
  801. }
  802.  
  803. int TreeWnd::expandItem(TreeItem *item) {
  804.   ASSERT(item != NULL);
  805.  
  806.   return item->expand();
  807. }
  808.  
  809. void TreeWnd::expandItemDeferred(TreeItem *item) {
  810.   postDeferredCallback(DC_EXPAND, (int)item);
  811. }
  812.  
  813. int TreeWnd::collapseItem(TreeItem *item) {
  814.   ASSERT(item != NULL);
  815.  
  816.   return item->collapse();
  817. }
  818.  
  819. void TreeWnd::collapseItemDeferred(TreeItem *item) {
  820.   postDeferredCallback(DC_COLLAPSE, (int)item);
  821. }
  822.  
  823. TreeItem *TreeWnd::getCurItem() {
  824.   return curSelected;
  825. }
  826.  
  827. int TreeWnd::getItemRect(TreeItem *item, RECT *r) {
  828.   ASSERT(item != NULL);
  829.  
  830.   return item->getCurRect(r);
  831. }
  832.  
  833. void TreeWnd::editItemLabel(TreeItem *item) {
  834.  
  835.   if (edited) {
  836.     edited->setEdition(FALSE);
  837.     edited->invalidate();
  838.   }
  839.  
  840.  
  841.   ASSERT(item != NULL);
  842.   if (item == NULL) return;
  843.  
  844.   if (item->onBeginLabelEdit()) return;
  845.   item->setEdition(TRUE);
  846.   edited = item;
  847.  
  848.   editwnd = new EditWnd();
  849.   editwnd->setModal(TRUE);
  850.   editwnd->setAutoSelect(TRUE);
  851.   editwnd->setStartHidden(TRUE);
  852.   editwnd->init(gethInstance(), gethWnd());
  853.   editwnd->setParent(this);
  854.   RECT r;
  855.   edited->getCurRect(&r);
  856.   RECT cr;
  857.   getClientRect(&cr);
  858.   r.right = cr.right;
  859.   if (r.bottom - r.top < 24) r.bottom = r.top + 24;
  860.   editwnd->resize(&r);
  861.   STRCPY(editbuffer, edited->getLabel());
  862.   editwnd->setBuffer(editbuffer, 255);
  863.   editUpdate();
  864.   editwnd->setVisible(TRUE);
  865. }
  866.  
  867. void TreeWnd::endEditLabel(char *newlabel) {
  868.   editwnd = NULL; // editwnd self destructs
  869.   if (edited->onEndLabelEdit(newlabel))
  870.     edited->setLabel(newlabel);
  871.   edited->setEdition(FALSE);
  872.   edited->invalidate();
  873.   onLabelChange(edited);
  874.   edited = NULL;
  875.   invalidateMetrics();
  876.   setSlidersPosition();
  877. }
  878.  
  879. void TreeWnd::cancelEditLabel(int destroyit) {
  880.   ASSERT(edited != NULL);
  881.   if (!edited) return;
  882.  
  883.   if (destroyit)
  884.     delete editwnd;
  885.  
  886.   editwnd = NULL; // editwnd self destructs (update> except if destroyit for cancelling from treewnd)
  887.   edited->setEdition(FALSE);
  888.   edited->invalidate();
  889.   edited = NULL;
  890. }
  891.  
  892. void TreeWnd::setAutoEdit(int ae) {
  893.   autoedit = ae;
  894. }
  895.  
  896. int TreeWnd::getAutoEdit() {
  897.   return autoedit;
  898. }
  899.  
  900. TreeItem *TreeWnd::getByLabel(TreeItem *item, char *name) {
  901.   TreeItem *ti;
  902.   // handle root-level searching
  903.   if (item == NULL) {
  904.     int n = getNumRootItems();
  905.     for (int i = 0; i < n; i++) {
  906.       ti = enumRootItem(i);
  907.       if (STREQL(name, ti->getLabel())) return ti;
  908.       ti = getByLabel(ti, name);
  909.       if (ti) return ti;
  910.     }
  911.     return NULL;
  912.   }
  913.  
  914.   // check the given item
  915.   if (STREQL(name, item->getLabel())) return item;
  916.  
  917.   // depth first search
  918.   ti = item->getChild();
  919.   if (ti != NULL) {
  920.     ti = getByLabel(ti, name);
  921.     if (ti != NULL) return ti;
  922.   }
  923.  
  924.   // recursively check siblings
  925.   ti = item->getSibling();
  926.   if (ti != NULL) ti = getByLabel(ti, name);
  927.  
  928.   return ti;
  929. }
  930.  
  931. int TreeWnd::onKillFocus() {
  932.  
  933.   TREEWND_PARENT::onKillFocus();
  934.   mousedown_item=NULL;
  935. /*  if (edited)
  936.     cancelEditLabel();*/
  937.     
  938.   return 1;
  939. }
  940.  
  941. int TreeWnd::onChar(char c) {
  942.   int r = 0;
  943.  
  944.   if (c == 27) {
  945.     if (edited)
  946.       cancelEditLabel(1);
  947.   }
  948.  
  949.   if (curSelected != NULL && (r = curSelected->onChar(c)) != 0) return r;
  950.  
  951.   char b = TOUPPER(c);
  952.   if (b >= 'A' && b <= 'Z') {
  953.     jumpToNext(b);
  954.     r = 1;
  955.   }
  956.  
  957.   return r ? r : TREEWND_PARENT::onChar(c);
  958. }
  959.  
  960. int TreeWnd::getNumVisibleChildItems(TreeItem *c) {
  961.   int nb=0;
  962.   for(int i=0;i<c->getNumChildren();i++) {
  963.     TreeItem *t=c->getNthChild(i);
  964.     if(t->hasSubItems() && t->isExpanded())
  965.       nb+=getNumVisibleChildItems(t);
  966.     nb++;
  967.   }
  968.   return nb;
  969. }
  970.  
  971. int TreeWnd::getNumVisibleItems() {
  972.   int nb=0;
  973.   for(int i=0;i<items.getNumItems();i++) {
  974.     TreeItem *t=items.enumItem(i);
  975.     if(t->hasSubItems() && t->isExpanded())
  976.       nb+=getNumVisibleChildItems(t);
  977.     nb++;
  978.   }
  979.   return nb;
  980. }
  981.  
  982. TreeItem *TreeWnd::enumVisibleChildItems(TreeItem *c, int n) {
  983.   int nb=0;
  984.   for(int i=0;i<c->getNumChildren();i++) {
  985.     TreeItem *t=c->getNthChild(i);
  986.     if(nb==n) return t;
  987.     if(t->hasSubItems() && t->isExpanded()) {
  988.       TreeItem *t2=enumVisibleChildItems(t, n-nb-1);
  989.       if(t2) return t2;
  990.       nb+=getNumVisibleChildItems(t);
  991.     }
  992.     nb++;
  993.   }
  994.   return NULL;
  995. }
  996.  
  997. TreeItem *TreeWnd::enumVisibleItems(int n) {
  998.   int nb=0;
  999.   for(int i=0;i<items.getNumItems();i++) {
  1000.     TreeItem *t=items.enumItem(i);
  1001.     if(nb==n) return t;
  1002.     if(t->hasSubItems() && t->isExpanded()) {
  1003.       TreeItem *t2=enumVisibleChildItems(t, n-nb-1);
  1004.       if(t2) return t2;
  1005.       nb+=getNumVisibleChildItems(t);
  1006.     }
  1007.     nb++;
  1008.   }
  1009.   return NULL;
  1010. }
  1011.  
  1012. int TreeWnd::onKeyDown(int keycode) {
  1013.   switch(keycode) {
  1014.   case 113: {
  1015.     TreeItem *item = getCurItem();
  1016.     if (item)
  1017.       item->editLabel();
  1018.     return 1;
  1019.     }
  1020.   case VK_UP: {
  1021.     TreeItem *t=getCurItem();
  1022.     int l=getNumVisibleItems();
  1023.     for(int i=0;i<l;i++)
  1024.       if(enumVisibleItems(i)==t) {
  1025.         if(i-1>=0) {
  1026.           TreeItem *t2=enumVisibleItems(i-1);
  1027.           if(t2) setCurItem(t2,FALSE,FALSE);
  1028.         }
  1029.       }
  1030.     return 1;
  1031.     }
  1032.   case VK_DOWN: {
  1033.     TreeItem *t=getCurItem();
  1034.     int l=getNumVisibleItems();
  1035.     for(int i=0;i<l;i++)
  1036.       if(enumVisibleItems(i)==t) {
  1037.         TreeItem *t2=enumVisibleItems(i+1);
  1038.         if(t2) setCurItem(t2,FALSE,FALSE);
  1039.       }
  1040.     return 1;
  1041.     }
  1042.   case VK_PRIOR: {
  1043.     TreeItem *t=getCurItem();
  1044.     int l=getNumVisibleItems();
  1045.     for(int i=0;i<l;i++)
  1046.       if(enumVisibleItems(i)==t) {
  1047.         int a=MAX(i-5,0);
  1048.         TreeItem *t2=enumVisibleItems(a);
  1049.         if(t2) setCurItem(t2,FALSE,FALSE);
  1050.       }
  1051.     return 1;
  1052.     }
  1053.   case VK_NEXT: {
  1054.     TreeItem *t=getCurItem();
  1055.     int l=getNumVisibleItems();
  1056.     for(int i=0;i<l;i++)
  1057.       if(enumVisibleItems(i)==t) {
  1058.         int a=MIN(i+5,l-1);
  1059.         TreeItem *t2=enumVisibleItems(a);
  1060.         if(t2) setCurItem(t2,FALSE,FALSE);
  1061.       }
  1062.     return 1;
  1063.     }
  1064.   case VK_HOME: {
  1065.     TreeItem *t=enumVisibleItems(0);
  1066.     if(t) setCurItem(t,FALSE,FALSE);
  1067.     return 1;
  1068.     }
  1069.   case VK_END: {
  1070.     TreeItem *t=enumVisibleItems(getNumVisibleItems()-1);
  1071.     if(t) setCurItem(t,FALSE,FALSE);
  1072.     return 1;
  1073.     }
  1074.   case VK_LEFT: {
  1075.     TreeItem *t=getCurItem();
  1076.     if(t) t->collapse();
  1077.     return 1;
  1078.     }
  1079.   case VK_RIGHT: {
  1080.     TreeItem *t=getCurItem();
  1081.     if(t) t->expand();
  1082.     return 1;
  1083.     }
  1084.   }
  1085.   
  1086.   return TREEWND_PARENT::onKeyDown(keycode);
  1087. }
  1088.  
  1089. void TreeWnd::jumpToNext(char c) {
  1090.   firstFound=FALSE;
  1091.   if (jumpToNextSubItems(&items, c)) return;
  1092.   firstFound=TRUE;
  1093.   jumpToNextSubItems(&items, c);
  1094. }
  1095.  
  1096. int TreeWnd::jumpToNextSubItems(TreeItemList *list, char c) {
  1097.  
  1098.   for (int i=0;i<list->getNumItems();i++) {
  1099.  
  1100.     TreeItem *nextitem = list->enumItem(i);
  1101.     const char *l = nextitem->getLabel();
  1102.     char b = l ? TOUPPER(*l) : 0;
  1103.     if (b == c && firstFound) {
  1104.       selectItem(nextitem);
  1105.       nextitem->ensureVisible();
  1106.       return 1;
  1107.     }
  1108.  
  1109.     if (nextitem->isSelected()) firstFound = TRUE;
  1110.  
  1111.     if (nextitem->isExpanded())
  1112.       if (jumpToNextSubItems(nextitem->subitems, c)) return 1;
  1113.   }
  1114.   return 0;
  1115. }
  1116.  
  1117. void TreeWnd::ensureItemVisible(TreeItem *item) {
  1118.   ASSERT(item != NULL);
  1119.  
  1120.   // walk the parent tree to make sure item is visible
  1121.   for (TreeItem *cur = item->getParent(); cur; cur = cur->getParent()) {
  1122.     if (cur->isCollapsed()) cur->expand();
  1123.   }
  1124.  
  1125.   RECT r;
  1126.   RECT c;
  1127.   item->getCurRect(&r);
  1128.   getClientRect(&c);
  1129.   if (r.top < c.top || r.bottom > c.bottom) {
  1130.     if (r.top + (c.bottom - c.top) <= getContentsHeight())
  1131.       scrollToY(r.top);
  1132.     else {
  1133.       scrollToY(getContentsHeight()-(c.bottom-c.top));
  1134.     }
  1135.   }
  1136. }
  1137.  
  1138. void TreeWnd::setHilitedColor(const char *colorname) {
  1139.   // we have to store it in a String because SkinColor does not make a copy
  1140.   hilitedColorName = colorname;
  1141.   hilitedColor = hilitedColorName;
  1142. }
  1143.  
  1144. COLORREF TreeWnd::getHilitedColor() {
  1145.   return hilitedColor;
  1146. }
  1147.  
  1148. void TreeWnd::freeResources() {
  1149.   TREEWND_PARENT::freeResources(); 
  1150.     if (tabClosed) delete tabClosed;
  1151.     if (tabOpen) delete tabOpen;
  1152.     if (linkTopBottom) delete linkTopBottom;
  1153.     if (linkTopRight) delete linkTopRight;
  1154.     if (linkTopRightBottom) delete linkTopRightBottom;
  1155.     if (linkTabTopBottom) delete linkTabTopBottom;
  1156.     if (linkTabTopRight) delete linkTabTopRight;
  1157.     if (linkTabTopRightBottom) delete linkTabTopRightBottom;
  1158.     tabClosed = NULL;
  1159.     tabOpen = NULL;
  1160.     linkTopBottom = NULL;
  1161.     linkTopRight = NULL;
  1162.     linkTopRightBottom = NULL;
  1163.     linkTabTopBottom = NULL;
  1164.     linkTabTopRight = NULL;
  1165.     linkTabTopRightBottom = NULL;
  1166. }
  1167.  
  1168. void TreeWnd::reloadResources() {
  1169.   TREEWND_PARENT::reloadResources(); 
  1170.     tabClosed = new SkinBitmap("studio.tree.tab.closed");
  1171.     tabOpen = new SkinBitmap("studio.tree.tab.open");
  1172.  
  1173.     linkTopBottom = new SkinBitmap("studio.tree.link.top.bottom");
  1174.     linkTopRight = new SkinBitmap("studio.tree.link.top.right");
  1175.     linkTopRightBottom = new SkinBitmap("studio.tree.link.top.rightBottom");
  1176.     linkTabTopBottom = new SkinBitmap("studio.tree.link.tab.top.bottom");
  1177.     linkTabTopRight = new SkinBitmap("studio.tree.link.tab.top.right");
  1178.     linkTabTopRightBottom = new SkinBitmap("studio.tree.link.tab.top.rightBottom");
  1179. }
  1180.  
  1181. int TreeWnd::compareItem(TreeItem *p1, TreeItem *p2) {
  1182.   return STRCMP(p1->getLabel(), p2->getLabel());
  1183. }
  1184.  
  1185. int TreeWnd::setFontSize(int newsize) {
  1186.   TREEWND_PARENT::setFontSize(newsize);
  1187.   if (newsize >= 0) textsize = newsize;
  1188.   DCCanvas *c = new DCCanvas();
  1189.   HDC dc = GetDC(NULL); // Nonportable. 
  1190.   c->cloneDC(dc);
  1191.   c->pushTextSize(getFontSize());
  1192.   itemHeight = c->getTextHeight();
  1193.   c->popTextSize();
  1194.   delete c;
  1195.   ReleaseDC(NULL, dc);
  1196.   redraw = 1;
  1197.   metrics_ok = 0;
  1198.   invalidate();
  1199.   return 1;
  1200. }
  1201.  
  1202. int TreeWnd::getFontSize() {
  1203.   return textsize + api->metrics_getDelta();
  1204. }
  1205.  
  1206. ////////////////////////////////////////////////////////////////////////////////////
  1207. // TreeItem
  1208. ////////////////////////////////////////////////////////////////////////////////////
  1209.  
  1210. TreeItem::TreeItem() {
  1211.   parent=NULL;
  1212.   subitems = new TreeItemList();
  1213.   MEMZERO(&curRect, sizeof(RECT));
  1214.   childTab = TAB_AUTO;
  1215.   tree = NULL;
  1216.   expandStatus = STATUS_COLLAPSED;
  1217.   icon = NULL;
  1218.   _z = 0;
  1219.  
  1220.   selected = FALSE;
  1221.   hilitedDrop = FALSE;
  1222.   hilited = FALSE;
  1223.   being_edited = FALSE;
  1224.  
  1225.   setSorted(TRUE);
  1226. }
  1227.  
  1228. TreeItem::~TreeItem() {
  1229.   // the subitem will call parent tree which will remove item from our list
  1230.   deleteSubitems();
  1231.  
  1232.   // remove from parent tree
  1233.   if (tree) tree->removeTreeItem(this);
  1234.  
  1235.   delete subitems;
  1236.  
  1237.   delete icon;
  1238. }
  1239.  
  1240. void TreeItem::deleteSubitems() {
  1241.   while (subitems->getNumItems() > 0) {
  1242.     delete subitems->enumItem(0);
  1243.   }
  1244. }
  1245.  
  1246. void TreeItem::setSorted(int issorted) {
  1247.   subitems->setAutoSort(issorted);
  1248. }
  1249.  
  1250. void TreeItem::setChildTab(int haschildtab) {
  1251.   childTab = haschildtab;
  1252. }
  1253.  
  1254. void TreeItem::linkTo(TreeItem *par) {
  1255.   parent = par;
  1256.  
  1257.   if (par == NULL) return;
  1258.  
  1259.   par->addSubItem(this);
  1260. }
  1261.  
  1262. void TreeItem::addSubItem(TreeItem *item) {
  1263.   subitems->addItem(item);
  1264. }
  1265.  
  1266. int TreeItem::removeSubitem(TreeItem *item) {
  1267.   if (subitems->searchItem(item) == -1) return 0;
  1268.   subitems->removeItem(item);
  1269.   if (tree->redraw)
  1270.     tree->invalidate();
  1271.   return 1;
  1272. }
  1273.  
  1274. TreeItem *TreeItem::getChild() {
  1275.   return subitems->getFirst();
  1276. }
  1277.  
  1278. TreeItem *TreeItem::getChildSibling(TreeItem *item) { // locate item in children and return its sibling
  1279.   for (int i=0;i<subitems->getNumItems();i++) {
  1280.     if (subitems->enumItem(i) == item) {
  1281.       if (i == subitems->getNumItems()-1) return NULL;
  1282.       return subitems->enumItem(i+1);
  1283.     }
  1284.   }
  1285. return NULL;
  1286. }
  1287.  
  1288. TreeItem *TreeItem::getSibling() { // returns next item
  1289.   if (!parent)
  1290.     return tree->getSibling(this);
  1291.   else
  1292.     return parent->getChildSibling(this);
  1293. }
  1294.  
  1295. void TreeItem::setTree(TreeWnd *newtree) {
  1296.   tree = newtree;
  1297.   // recursively reset tree for children, if any
  1298.   for (int i = 0; ; i++) {
  1299.     TreeItem *item = getNthChild(i);
  1300.     if (item == NULL) break;
  1301.     item->setTree(tree);
  1302.   }
  1303. }
  1304.  
  1305. void TreeItem::ensureVisible() {
  1306.   if (tree) tree->ensureItemVisible(this);
  1307. }
  1308.  
  1309. const char *TreeItem::getLabel() {
  1310.   return label;
  1311. }
  1312.  
  1313. void TreeItem::setLabel(const char *newlabel) {
  1314.   label = newlabel;
  1315.   if (newlabel) {
  1316.     if (tree)
  1317.       tree->invalidateMetrics();
  1318.     invalidate();
  1319.   }
  1320. }
  1321.  
  1322. int TreeItem::customDraw(Canvas *canvas, const POINT &pt, int txtHeight, int indentation, const RECT &clientRect) {
  1323.  
  1324.   if (being_edited) return 0;
  1325.  
  1326.   SkinBitmap *icon = getIcon();
  1327.  
  1328.   int startPoint = pt.x - indentation;
  1329.  
  1330.   if (isSelected() || isHilitedDrop()) {
  1331.     RECT r;
  1332.     r.left = pt.x;
  1333.     r.top = pt.y;
  1334.     r.right = r.left + canvas->getTextWidth(label)+2+(icon ? txtHeight : 0);
  1335.     r.bottom = r.top + txtHeight;
  1336.     canvas->fillRect(&r, isHilitedDrop() ? drophilitecolor : selectedcolor);
  1337.   }
  1338.  
  1339.   if (isHilited()) {
  1340.     RECT r;
  1341.     r.left = pt.x;
  1342.     r.top = pt.y;
  1343.     r.right = r.left + canvas->getTextWidth(label)+2+(icon ? txtHeight : 0);
  1344.     r.bottom = r.top + txtHeight;
  1345.     canvas->drawRect(&r, 1, tree->getHilitedColor());
  1346.   }
  1347.  
  1348.   POINT d=pt;
  1349.  
  1350.   if (icon) {
  1351.     RECT i;
  1352.     i.left = pt.x+1;
  1353.     i.right = i.left + txtHeight-2;
  1354.     i.top = pt.y+1;
  1355.     i.bottom = i.top + txtHeight-2;
  1356.     icon->stretchToRectAlpha(canvas, &i);
  1357.     d.x += txtHeight;
  1358.   }
  1359.  
  1360. //CUT  int x = pt.x;
  1361.  
  1362.   canvas->textOut(d.x+1, d.y, label);
  1363.  
  1364.   return canvas->getTextWidth(label)+2+(icon ? txtHeight : 0);
  1365. }
  1366.  
  1367. int TreeItem::getItemWidth(int txtHeight, int indentation) {
  1368.   SkinBitmap *icon = getIcon();
  1369.   if (!label) return (icon ? txtHeight : 0);
  1370.   DCCanvas *c = new DCCanvas();
  1371.   HDC dc = GetDC(NULL); //Nonportable. 
  1372.   c->cloneDC(dc);
  1373.   c->pushTextSize(tree->getFontSize());
  1374.   int width = c->getTextWidth(label)+2;  
  1375.   c->popTextSize();
  1376.   delete c;
  1377.   ReleaseDC(NULL, dc);
  1378.   width += (icon ? txtHeight : 0);
  1379.   return width;
  1380. }
  1381.  
  1382. int TreeItem::getNumChildren() {
  1383.   return subitems->getNumItems();
  1384. }
  1385.  
  1386. TreeItem *TreeItem::getNthChild(int nth) {
  1387.   if (nth >= subitems->getNumItems()) return NULL;
  1388.   return subitems->enumItem(nth);
  1389. }
  1390.  
  1391. void TreeItem::setCurRect(int x1, int y1, int x2, int y2, int z) {
  1392.   curRect.left = x1;
  1393.   curRect.right = x2;
  1394.   curRect.top = y1;
  1395.   curRect.bottom = y2;
  1396.   _z = z;
  1397. }
  1398.  
  1399. int TreeItem::getIndent() {
  1400.   return _z;
  1401. }
  1402.  
  1403. BOOL TreeItem::hasSubItems() {
  1404.   return subitems->getNumItems()>0;
  1405. }
  1406.  
  1407. BOOL TreeItem::needTab() {
  1408.   return (childTab == TAB_YES || (childTab == TAB_AUTO && hasSubItems()));
  1409. }
  1410.  
  1411. BOOL TreeItem::isExpanded() {
  1412.   if (!hasSubItems()) return FALSE;
  1413.   return expandStatus == STATUS_EXPANDED;
  1414. }
  1415.  
  1416. BOOL TreeItem::isCollapsed() {
  1417.   if (!hasSubItems()) return TRUE;
  1418.   return expandStatus == STATUS_COLLAPSED;
  1419. }
  1420.  
  1421. int TreeItem::getCurRect(RECT *r) {
  1422.   r->left = curRect.left-tree->getScrollX();
  1423.   r->top = curRect.top-tree->getScrollY();
  1424.   r->right = curRect.right-tree->getScrollX();
  1425.   r->bottom = curRect.bottom-tree->getScrollY();
  1426.   RECT c;
  1427.   tree->getClientRect(&c);
  1428.   r->top += c.top;
  1429.   r->bottom += c.top;
  1430.   r->left += c.left;
  1431.   r->right += c.left;
  1432.   return 1;
  1433. }
  1434.  
  1435. TreeWnd *TreeItem::getTree() const {
  1436.   return tree;
  1437. }
  1438.  
  1439. void TreeItem::setSelected(int isSelected, BOOL expandCollapse, BOOL editifselected) {
  1440.   int wasselected = selected;
  1441.   selected = !!isSelected;
  1442.  
  1443.   if (selected != wasselected) {
  1444.     invalidate();
  1445.     tree->repaint();
  1446.     if (selected) {
  1447.       onSelect();
  1448.       ASSERT(tree != NULL);
  1449.       tree->onItemSelected(this);
  1450.     } else {
  1451.       onDeselect();
  1452.       ASSERT(tree != NULL);
  1453.       tree->onItemDeselected(this);
  1454.     }
  1455.   } else {
  1456.     if (selected && editifselected) {
  1457.       editLabel();
  1458.     }
  1459.   }
  1460.  
  1461.   if (expandCollapse) {
  1462.     if (isCollapsed())
  1463.       expand();
  1464.     else
  1465.       collapse();
  1466.   }
  1467. }
  1468.  
  1469. void TreeItem::invalidate() {
  1470.   if (tree) {
  1471.     RECT r;
  1472.     getCurRect(&r);
  1473.     tree->invalidateRect(&r);
  1474.   }
  1475. }
  1476.  
  1477. BOOL TreeItem::isSelected() {
  1478.   return selected;
  1479. }
  1480.  
  1481. int TreeItem::collapse() {
  1482.   if (!hasSubItems()) return 0;
  1483.   int old = expandStatus;
  1484.   if (expandStatus == STATUS_COLLAPSED)
  1485.     return 0;
  1486.   expandStatus = STATUS_COLLAPSED;
  1487.   RECT c;
  1488.   tree->getClientRect(&c);
  1489.   RECT r;
  1490.   getCurRect(&r);
  1491.   r.bottom = c.bottom;
  1492.   r.left = c.left;
  1493.   r.right = c.right;
  1494.   if (tree) {
  1495.       tree->invalidateRect(&r);
  1496.       tree->invalidateMetrics();
  1497.   }
  1498.  
  1499.   return old != expandStatus;
  1500. }
  1501.  
  1502. int TreeItem::expand() {
  1503.  
  1504.   if (!hasSubItems()) return 0;
  1505.   int old = expandStatus;
  1506.   if (expandStatus == STATUS_EXPANDED)
  1507.     return 0;
  1508.   expandStatus = STATUS_EXPANDED;
  1509.   RECT c;
  1510.   tree->getClientRect(&c);
  1511.   RECT r;
  1512.   getCurRect(&r);
  1513.   r.bottom = c.bottom;
  1514.   r.left = c.left;
  1515.   r.right = c.right;
  1516.   if (tree) {
  1517.       tree->invalidateRect(&r);
  1518.       tree->invalidateMetrics();
  1519.   }
  1520.  
  1521.   return old != expandStatus;
  1522. }
  1523.  
  1524. int TreeItem::onContextMenu(int x, int y) { 
  1525.   return 0; 
  1526. }
  1527.  
  1528. void TreeItem::setHilitedDrop(BOOL ishilitedDrop) {
  1529.   int washilighted = hilitedDrop;
  1530.   hilitedDrop = !!ishilitedDrop;
  1531.  
  1532.   if (washilighted != hilitedDrop)
  1533.     invalidate();
  1534. }
  1535.  
  1536. void TreeItem::setHilited(BOOL ishilited) {
  1537.   int washilighted = hilited;
  1538.   hilited = !!ishilited;
  1539.  
  1540.   if (washilighted != hilited)
  1541.     invalidate();
  1542. }
  1543.  
  1544. TreeItem *TreeItem::getParent() {
  1545.   return parent;
  1546. }
  1547.  
  1548. BOOL TreeItem::isHilitedDrop() {
  1549.   return hilitedDrop;
  1550. }
  1551.  
  1552. BOOL TreeItem::isHilited() {
  1553.   return hilited;
  1554. }
  1555.  
  1556. void TreeItem::setIcon(SkinBitmap *newicon) {
  1557.   if (icon) {
  1558.     delete icon;
  1559.     icon = NULL;
  1560.   }
  1561.   icon = newicon;
  1562.   invalidate();
  1563. }
  1564.  
  1565. SkinBitmap *TreeItem::getIcon() {
  1566.   return icon;
  1567. }
  1568.  
  1569. void TreeItem::sortItems() {
  1570.   subitems->sort();
  1571. }
  1572.  
  1573. void TreeItem::editLabel() {
  1574.   if (!tree) return;
  1575.   tree->editItemLabel(this);
  1576. }
  1577.  
  1578. int TreeItem::onBeginLabelEdit() {
  1579.   return 1; // disable editing by default
  1580. }
  1581.  
  1582. int TreeItem::onEndLabelEdit(const char *newlabel) {
  1583.   return 1; // accept new label by default
  1584. }
  1585.  
  1586. void TreeItem::setEdition(BOOL isedited) {
  1587.   being_edited = !!isedited;
  1588.   invalidate();
  1589. }
  1590.  
  1591. BOOL TreeItem::getEdition() {
  1592.   return being_edited;
  1593. }
  1594.  
  1595. BOOL TreeItem::isSorted() {
  1596.   ASSERT(subitems == NULL);
  1597.   return subitems->getAutoSort();
  1598. }
  1599.  
  1600.